Extract Spring Boot 2.x app source into nested-build subprojects#11408
Extract Spring Boot 2.x app source into nested-build subprojects#11408bric3 wants to merge 6 commits into
Conversation
Adds a new included build `build-logic/` hosting a single subproject
`smoke-test` that exposes the `dd-trace-java.smoke-test-app` plugin.
The plugin contributes:
- `NestedGradleBuild` task type that runs a nested Gradle build via the
Gradle Tooling API. It pins the nested Gradle version (no committed
per-application wrappers), uses the configured Java toolchain for the
nested daemon, forwards artifact paths from the root build as
`-P<name>=<path>`, and redirects the nested `buildDir` via
`-PappBuildDir=<path>` so outputs land under the outer project's build
directory.
- `smokeTestApp` project extension with an `application { ... }` block
that registers the `NestedGradleBuild` task, wires it into every `Test`
task via `dependsOn` + a `jvmArgumentProvider` for the produced
artifact's system property. Consumers can also register
`NestedGradleBuild` directly when they need more control; the plugin
is a no-op until `application` or a manual registration is done.
- `projectJar(name, project)` helper that forwards a sibling project's
jar to the nested build through a resolvable `Configuration` (avoids
`evaluationDependsOn` and the cross-project access ordering issues).
The plugin is verified with JUnit 5 unit tests (`ProjectBuilder`) and
end-to-end tests that drive the Tooling API path through the Gradle Test
Kit with a temporary Kotlin-DSL test project.
`build-logic/settings.gradle.kts` references the existing
`gradle/libs.versions.toml` catalog (mirroring `buildSrc/`) so the
plugin can use the same library coordinates as the rest of the repo.
The Gradle libs Maven repository (`https://repo.gradle.org/gradle/libs-releases`,
scoped to `org.gradle:`) is added to the root build's `pluginManagement`
and to `gradle/repositories.gradle` so the Tooling API jar resolves.
Smoke-test modules with Spring Boot plugin versions incompatible with
Gradle 9 will use this plugin in follow-up PRs instead of a committed
Gradle 8 wrapper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
b924cb8 to
149a0ec
Compare
…14.5
Set conventions on `smokeTestApp`:
- `gradleVersion` defaults to `"8.14.5"` (Gradle 8 last release; pinned because
Spring Boot plugin pre-3.5 calls `Configuration.getUploadTaskName()`, removed
in Gradle 9).
- `javaLauncher` defaults to a JDK 21 toolchain (the version the root build
requires for its own Gradle 9 migration; standardising the nested daemon on
the same JDK avoids requiring an extra toolchain on dev machines and CI
runners).
Consumers that need a different JDK or Gradle version still override
explicitly. The inner build script is responsible for pinning the produced
bytecode level (`java { sourceCompatibility = JavaVersion.VERSION_1_8 }` or
similar) — Gradle adds `--release N` automatically when source/target differs
from the daemon JVM.
`JavaToolchainService` is now injected into the extension; this works in any
project where a `java*` (or related) plugin is applied. Smoke-test modules
already apply `gradle/java.gradle`, which applies `java`, so the convention
resolves on first read.
Public defaults exposed as `DEFAULT_NESTED_GRADLE_VERSION` and
`DEFAULT_NESTED_JAVA_VERSION` constants so the values are discoverable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
c6d9744 to
1562192
Compare
|
9e60b33 to
6364e58
Compare
There was a problem hiding this comment.
note: settings.gradle in nested projects have somewhat the same duplicated configuration around repo proxies. And ci cache. I have no proper solution yet, but this should come as a follow-up work.
…ugin
Switch the plugin sources and unit tests over to the typed
`org.gradle.kotlin.dsl` extension functions where they replace
`::class.java` boilerplate:
- `tasks.register(name, Type::class.java) { … }` → `tasks.register<Type>(name) { … }`
- `tasks.withType(Type::class.java).configureEach { … }` → `tasks.withType<Type>().configureEach { … }`
- `extensions.create("name", Type::class.java)` → `extensions.create<Type>("name")`
- `extensions.getByType(Type::class.java)` → `extensions.getByType<Type>()`
- `extensions.findByName("name")` (followed by `isInstanceOf`) → `extensions.findByType<Type>()`
- `project.plugins.apply(Plugin::class.java)` → `project.apply<Plugin>()` (PluginAware)
- `objects.newInstance(Type::class.java)` → `objects.newInstance<Type>()`
Also drop the six `captured*` local variables in `SmokeTestAppExtension.application` —
inside `tasks.register<NestedGradleBuild>(taskName) { … }` the outer extension's
properties are now reached via `this@SmokeTestAppExtension.<prop>` directly.
No behavioural change; the 9 plugin tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ild subprojects The Spring Boot Gradle plugin is incompatible with Gradle 9 for all versions before 3.5.0 because it calls `Configuration.getUploadTaskName()`, a method removed in Gradle 9. Twelve smoke-test modules were direct Gradle subprojects that applied the Spring Boot plugin to build their bootJar / bootWar artefact. They cannot stay as subprojects of the Gradle 9 root build. For each of these modules, the application source is extracted into a new `application/` subdirectory that is a fully self-contained Gradle project (`settings.gradle` + `build.gradle`). The outer module keeps the test source and delegates the application build to the `dd-trace-java.smoke-test-app` plugin from `build-logic/` (added in the parent infrastructure PR), which runs the nested build via the Gradle Tooling API pinned to Gradle 8.14.5 — no committed `gradlew` wrapper. Modules converted (bootJar): - springboot-thymeleaf (Spring Boot 2.7.15, Java 8) - springboot-freemarker (Spring Boot 2.7.15 plugin / 1.5.18 starter, Java 8) - springboot-velocity (Spring Boot 2.7.15 plugin / 1.5.18 starter, Java 8) - springboot-java-11 (Spring Boot 2.7.15, Java 11; passes iast-util-11 jar) - springboot-java-17 (Spring Boot 2.7.15, Java 17; passes iast-util-17 jar) - openfeature (Spring Boot 2.7.15, Java 11; passes feature-flagging-api jar) - kafka-2 (Spring Boot 2.7.15, Java 8; passes iast-util jar) - apm-tracing-disabled (Spring Boot 2.7.15, Java 8; passes dd-trace-api jar) Modules converted (bootWar): - springboot-jpa (Spring Boot 2.6.0, Java 8; Lombok) - springboot-tomcat-jsp (Spring Boot 2.7.15, Java 8; JSP webapp) - springboot-jetty-jsp (Spring Boot 2.7.15, Java 8; JSP webapp) - springboot-tomcat (Spring Boot 2.5.12, Java 8; Ivy Tomcat download + unzip) Where an application module depends on a project artifact from the main build (e.g. iast-util-11, dd-trace-api), the jar is forwarded via the plugin's `projectJar(name, project)` helper — a resolvable `Configuration` that establishes the upstream task dependency automatically. The inner build picks it up via `findProperty(name)` and adds it as `implementation files(...)`. The `spring-kafka-test` test dependency in kafka-2 is pinned to 2.8.11 (the version previously resolved from the Spring Boot BOM) since the outer test module no longer applies that BOM. For springboot-freemarker and springboot-velocity, the `XssController` loads templates from `resources/main/templates` relative to the test JVM's working directory. After moving sources into `application/`, those files land at `build/application/resources/main/templates/` instead of `build/resources/main/templates/`. Each affected outer build registers a `copyAppResources` Copy task that mirrors the inner build's processed resources into the outer build dir so the runtime path still resolves. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6364e58 to
6b7e87c
Compare
AlexeyKuznetsov-DD
left a comment
There was a problem hiding this comment.
Overall LGTM, left minor comments and questions.
I feel that all build files should have more comments around why things done in that way, also maybe there is a way to minimize config by using reasonable defaults here and there.
| smokeTestApp { | ||
| application { | ||
| taskName = 'bootJar' | ||
| artifactPath = 'libs/apm-tracing-disabled-smoketest.jar' |
There was a problem hiding this comment.
Just as idea: how about to calculate default name of artifactPath from module name by template?
Like (pseudocode): libs/$moduleName-smoke-test.jar? With all needed .toLowerCase() and replace if needed. WDYT?
There was a problem hiding this comment.
good point, I'll take a look in a follow up pr
| buildCache { | ||
| local { | ||
| directory = "$sharedRootDir/workspace/build-cache" | ||
| } | ||
| } |
There was a problem hiding this comment.
Let's comment the knowledge behind such non-trivial-Gradle configs.
There was a problem hiding this comment.
Note existing smoke tests already had this for quite some time, it just follows what's been there, it seems to have been introduced by b34ccbc, and has been applied to other smoke tests, prior my changes.
The buildcache dir config referes to the root level cache that was introduced in f6ec1f5 #982
I'll add the comments in a follow-up pr.
| java { | ||
| sourceCompatibility = 11 | ||
| targetCompatibility = 11 | ||
| } |
There was a problem hiding this comment.
Just curious why Java 11 with old spring boot 2.7.15?
There was a problem hiding this comment.
The app was compiled with Java 11 before
dd-trace-java/dd-smoke-tests/openfeature/build.gradle
Lines 17 to 20 in 5181a21
There was a problem hiding this comment.
If app compiled with Java 11, maybe Gradle plugins can be bumped?
| def sharedRootDir = "$rootDir/../../../" | ||
| buildCache { | ||
| local { | ||
| directory = "$sharedRootDir/workspace/build-cache" |
There was a problem hiding this comment.
Just curious why in some projects sharedRootDir hardcoded, and some under isCI?
|
/merge |
|
View all feedbacks in Devflow UI.
The expected merge time in
The merge request has been interrupted because the build 0 took longer than expected. The current limit for the base branch 'master' is 120 minutes. |
Address review feedback on #11408: add comments explaining the legacy Spring Boot 2.7.x / OpenTracing pins, the iastUtilJar property wired from the outer build, the CI-only shared build cache (f6ec1f5 / #982, b34ccbc), the internal Maven mirror proxies, and the Java 11 baseline for the OpenFeature smoke test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Match the review feedback applied on #11408: expand the terse comments on the nested settings.gradle files to explain the CI Maven mirror proxies and the CI-only shared build cache (f6ec1f5 / #982, b34ccbc), plus the Quarkus plugin version property forwarding. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| // root-level cache, and b34ccbc048 for the `isCI` gating — locally we keep the default | ||
| // per-user cache to avoid leaking entries into the repo tree. | ||
| if (isCI) { | ||
| def sharedRootDir = "$rootDir/../../../" |
There was a problem hiding this comment.
Just curious, why in some places sharedRooDir is under if (CI) and in some places it is not?
There was a problem hiding this comment.
Actually, the more I think about this, the more I think this build cache config is useless for nested smoke test projects. Given build cache is disabled by default and that the nested gradle jobs (existing or new via this PR) don't leverage the build-cache (there's neither --build-cache or org.gradle.caching=true). Consequently the buildCache { local { directory = … } } block is useless.
What Does This Do
For 12 smoke-test modules whose Spring Boot Gradle plugin is incompatible with Gradle 9, this PR extracts the application source into a self-contained
application/Gradle subproject and switches the outer module to thesmokeTestApp { application { … } }DSL added in #11405.Modules converted (bootJar):
springboot-thymeleaf(Spring Boot 2.7.15, Java 8)springboot-freemarker(Spring Boot 2.7.15 plugin / 1.5.18 starter, Java 8)springboot-velocity(Spring Boot 2.7.15 plugin / 1.5.18 starter, Java 8)springboot-java-11(Spring Boot 2.7.15, Java 11; passesiast-util-11jar)springboot-java-17(Spring Boot 2.7.15, Java 17; passesiast-util-17jar)openfeature(Spring Boot 2.7.15, Java 11; passesfeature-flagging-apijar)kafka-2(Spring Boot 2.7.15, Java 8; passesiast-utiljar)apm-tracing-disabled(Spring Boot 2.7.15, Java 8; passesdd-trace-apijar)Modules converted (bootWar):
springboot-jpa(Spring Boot 2.6.0, Java 8; Lombok)springboot-tomcat-jsp(Spring Boot 2.7.15, Java 8; JSP webapp)springboot-jetty-jsp(Spring Boot 2.7.15, Java 8; JSP webapp)springboot-tomcat(Spring Boot 2.5.12, Java 8; Ivy Tomcat download + unzip)Tip
For each module the change is uniform:
git mv src/main/ application/src/main/application/settings.gradle+application/build.gradlecarrying the Spring Boot plugin + the module's existing dependencies.build.gradlerewritten to applydd-trace-java.smoke-test-appand configuresmokeTestApp { application { … } }(plusprojectJar(name, project)for the 5 modules that forward a sibling jar).Note
smokeTestApphas a convention of running Gradle 8.14.5, with a daemon running on Java 21, so it's not explicitly set. It's configurable if needed.Motivation
The Spring Boot Gradle plugin pre-3.5.0 calls
Configuration.getUploadTaskName(), removed in Gradle 9. These 12 modules block the root Gradle 9 migration.Mirrors the goal of PR #11379 "Pattern C" (12 modules, source extraction), but instead of committing 22 per-application
gradlewwrappers it uses the Gradle Tooling API via thedd-trace-java.smoke-test-appplugin (#11405). Net diff per module is smaller; nothing extra is committed to source control beyond the innerapplication/Gradle scripts.Additional Notes
kafka-2:spring-kafka-testis pinned to 2.8.11 in the outer test module (was previously resolved from the Spring Boot BOM, which no longer applies to the outer module).springboot-freemarker/springboot-velocity: theirXssControllerloads templates fromresources/main/templatesrelative to the test JVM working directory. After the source move, the processed resources land underbuild/application/resources/main/. Each affected outer build adds acopyAppResourcesCopy task that mirrors them tobuild/resources/main/so the runtime path resolves.jardependsOn this Copy task to avoid an implicit-output conflict.springboot-tomcat: the Ivy-downloaded Tomcat server (apache-tomcat-9.0.117) stays inside the nested build; the outer build forwards the unpack directory as a second system property viaadditionalSystemProperties.apm-tracing-disabled:ApmTracingDisabledSamplingSmoke{V04,V1}Testare already@Flakyand continue to be skipped in CI../gradlew :dd-smoke-tests:<module>:test -PskipFlakyTests=truefor each module.Contributor Checklist
type:and (comp:orinst:) labels in addition to any other useful labels🤖 Generated with Claude Code